//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-02-28     ZhangYihua   first version
//
// Description  : 
//################################################################################

module iaa2mem #(      // indirect address access(read/write) memory
parameter           DEPTH                   = 4096,
parameter           DATA_BW                 = 32,
parameter           INC_BW                  = 1,    // address increment bit width
parameter           MEM_RL                  = 3,    // meory read latency, MEM_RL>=1

// the following parameters are calculated automatically
parameter           ADDR_BW                 = $clog2(DEPTH)
) ( 
input                                       rst_n,
input                                       clk,

input                                       iaa_b2b,        // 1:back-to-back, each rd/wr DATA reg triger one MEM RW; 0:normal; 
input                                       iaa_wr,         // 1'b1: wtite; 1'b0: read
input                                       iaa_start,      // 1 clock pulse to start a single RW
input               [ADDR_BW-1:0]           iaa_addr,
output                                      iaa_addr_bwe,   // auto inc iaa_addr if iaa_b2b==1'b1
output              [ADDR_BW-1:0]           iaa_addr_bwd,
input               [DATA_BW-1:0]           iaa_data,       // write data CPU -> mem
input                                       iaa_data_fwe,
input                                       iaa_data_fre,
output                                      iaa_data_bwe,
output              [DATA_BW-1:0]           iaa_data_bwd,   // read data CPU <- mem
input               [INC_BW-1:0]            iaa_addr_inc,   // address increment step at back-to-back mode
input                                       iaa_ctrl_fwe,   // forward write enable for CTRL(b2b/wr/start/addr) reg
input                                       iaa_data_frw,   // forward read or write for DATA reg
output                                      iaa_miss,       // mem_ack must fast enough avoid to miss next RW
output  reg                                 iaa_busy,

output  reg                                 mem_req,
output                                      mem_wr,
output              [ADDR_BW-1:0]           mem_addr,
output              [DATA_BW-1:0]           mem_wdat,
input               [DATA_BW-1:0]           mem_rdat,   // rdat is valid after MEM_RL from mem_ack for read
input                                       mem_ack
);

//################################################################################
// define local varialbe and localparam
//################################################################################
localparam [ADDR_BW-1:0]    ADDR_MAX        = DEPTH-1;

wire                                        b2b_start;
wire                                        req_start;
wire                                        req_done;
wire                                        rl_done;

//################################################################################
// main
//################################################################################

assign b2b_start    = (iaa_b2b==1'b0) ? 1'b0 :          // non-B2B mode
                      (iaa_wr==1'b1)  ? iaa_data_fwe :  // wr DATA reg trigger wr mem req for current address
                                        iaa_data_fre;   // rd DATA reg trigger rd mem req for next address

assign iaa_addr_bwe = (iaa_b2b==1'b0) ? 1'b0 :          // non-B2B mode
                      (iaa_wr==1'b1)  ? req_done :      // wr current address done trigger inc address
                                        iaa_data_fre;   // rd DATA reg trigger inc address

assign req_start = (~iaa_busy) & (iaa_start | b2b_start);
assign iaa_miss  =   iaa_busy  & (iaa_start | iaa_ctrl_fwe | iaa_data_frw);

assign req_done = mem_req & mem_ack;
always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        mem_req <=`U_DLY 1'b0;
    end else begin
        if (req_start==1'b1)
            mem_req <=`U_DLY 1'b1;
        else if (req_done==1'b1)
            mem_req <=`U_DLY 1'b0;
        else
            ;
    end
end

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        iaa_busy <=`U_DLY 1'b0;
    end else begin
        if (req_start==1'b1)
            iaa_busy <=`U_DLY 1'b1;
        else if ((mem_wr==1'b1) && (req_done==1'b1))
            iaa_busy <=`U_DLY 1'b0;
        else if (rl_done==1'b1)
            iaa_busy <=`U_DLY 1'b0;
        else
            ;
    end
end

generate if (MEM_RL==1) begin:G_RL1
    
    assign rl_done = (~mem_req) & iaa_busy;

end else begin:G_RLM
    localparam          RL_BW                   = $clog2(MEM_RL);
    localparam [RL_BW-1:0]      RL_MAX          = MEM_RL-1;
    localparam [RL_BW-1:0]      RL_ZERO         = 0;

    reg                 [RL_BW-1:0]             cnt_rl;

    always@(posedge clk or negedge rst_n) begin
        if (rst_n==1'b0) begin
            cnt_rl <=`U_DLY {RL_BW{1'b0}};
        end else begin
            if ((mem_wr==1'b0) && (req_done==1'b1))
                cnt_rl <=`U_DLY RL_MAX;
            else if (cnt_rl>RL_ZERO)
                cnt_rl <=`U_DLY cnt_rl - 1'd1;
            else
                ;
        end
    end
    assign rl_done = (cnt_rl==RL_ZERO) ? (~mem_req) & iaa_busy : 1'b0;
    
end endgenerate

generate if ((1<<ADDR_BW)==DEPTH) begin:G0
    assign iaa_addr_bwd = iaa_addr + iaa_addr_inc;
end else begin:G1
    localparam [ADDR_BW-1:0]    DEPTH_V         = DEPTH;
    wire                [ADDR_BW+1-1:0]         tmp_addr;

    assign tmp_addr = iaa_addr + iaa_addr_inc;

    assign iaa_addr_bwd = (tmp_addr<={1'b0, ADDR_MAX}) ? tmp_addr[0+:ADDR_BW] :
                                                         tmp_addr[0+:ADDR_BW] - DEPTH_V; 
end endgenerate

assign mem_wr   = iaa_wr;
assign mem_addr = iaa_addr;
assign mem_wdat = iaa_data;

assign iaa_data_bwd = mem_rdat;
assign iaa_data_bwe = rl_done;

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
